package com.oschina.sequence;

import com.oschina.sequence.exception.SequenceException;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;

/**
 * Created by limu on 16/6/28.
 */
public class SequenceTable {

    /**
     * db个数
     */
    private int dbSize;

    /**
     * 数据库序号
     */
    private int dbIndex;

    /**
     * 每个db中sequence表的个数
     */
    private int tableSize;

    /**
     * sequence表序号
     */
    private int tableIndex;

    /**
     * 步长
     */
    private int step;

    /**
     * 初始化值
     */
    private long initValue;

    /**
     * 当前值，必须是volatile，防止并发问题
     */
    private volatile long nextValue;

    /**
     * 起始值
     */
    private long startValue;

    /**
     * ID名称
     */
    private String sequenceName;


    public SequenceTable(int dbSize, int dbIndex, int tableSize, int tableIndex, int step, long initValue, String sequenceName) {
        this.dbSize = dbSize;
        this.dbIndex = dbIndex;
        this.tableSize = tableSize;
        this.tableIndex = tableIndex;
        this.step = step;
        this.initValue = initValue;
        this.sequenceName = sequenceName;
    }

    /**
     * 获取下一个新值
     *
     * @return
     * @throws SequenceException
     */
    public synchronized long nextValue() throws SequenceException {
        if (0 == nextValue) {
            throw new SequenceException("init nextValue error! nextValue=0");
        }

        // 如果已经增长到了临界值，悲观锁更新临界值，重新计算nextValue
        if ((nextValue - startValue) % step == 0) {
            nextValue = updateValue();
        }

        nextValue = nextValue + 1;

        return nextValue - 1;
    }

    public void init() throws SequenceException {
        //插入初值
        insertInitValue();

        // 从数据库更新，获取nextValue
        long value = updateValue();

        // 初始化startValue，nextValue值
        startValue = value;
        nextValue = value + 1;
    }

    /**
     * 事务更新值
     *
     * @return
     * @throws SequenceException
     */
    private long updateValue() throws SequenceException {
        Statement statement = null;
        ResultSet queryResult = null;
        Connection connection = DbPoolManger.getConnection(dbIndex);

        try {
            //step1 设置事务模式
            connection.setAutoCommit(false);

            //step2 执行查询，添加悲观锁
            String querySql = "select value from sequence" + tableIndex + " where name='" + sequenceName + "' for update;";
            statement = connection.createStatement();
            queryResult = statement.executeQuery(querySql);

            //step3 解析sql查询
            long queryValue = 0;
            if (queryResult.next()) {
                queryValue = queryResult.getLong(1);
            }
            if (0 == queryValue) {
                // 如果为0，抛出异常
                throw new SequenceException("query sequence value error! name=" + sequenceName);
            }

            //step4 查询成功，计算新值
            queryValue = queryValue + step * (dbSize * tableSize);

            //step5 进行更新
            String updateSql = "update sequence" + tableIndex + " set value=  " + queryValue + " where name='" + sequenceName + "';";
            int result = statement.executeUpdate(updateSql);
            if (result != 1) {
                // 更新出错，抛出异常
                throw new SequenceException("update sequence value error! name=" + sequenceName);
            }

            //step6 提交事务
            connection.commit();

            return queryValue;
        } catch (Exception e) {
            //step7 异常回滚
            try {
                connection.rollback();
            } catch (Exception e1) {
                throw new SequenceException(toString(), e1);
            }

            throw new SequenceException(toString(), e);
        } finally {
            // 关闭资源
            try {
                if (null != queryResult) {
                    queryResult.close();
                    queryResult = null;
                }

                if (null != statement) {
                    statement.close();
                    statement = null;
                }

                if (null != connection) {
                    connection.close();
                }
            } catch (Exception e) {
                throw new SequenceException(toString(), e);
            }
        }
    }

    /**
     * 无论如何，插入初值
     */
    private void insertInitValue() throws SequenceException {
        Statement statement = null;
        Connection connection = DbPoolManger.getConnection(dbIndex);

        try {
            //step1 设置自动提交模式，无需commit
            connection.setAutoCommit(true);

            //step2 计算初值
            long value = initValue + step * (dbIndex * tableSize + tableIndex);

            //step3 插入初值
            String sql = "insert into sequence" + tableIndex + "(name,value,gmt_create) values('" + sequenceName + "', '" + value + "', now());";
            statement = connection.createStatement();
            statement.executeUpdate(sql);
        } catch (Exception e) {
            // nothing to do
        } finally {
            // 关闭资源
            try {
                if (null != statement) {
                    statement.close();
                    statement = null;
                }

                if (null != connection) {
                    connection.close();
                }
            } catch (Exception e) {
                // nothing to do
            }
        }
    }

}
